<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    
    <title>python集合set底层原理 | 王鹏的技术博客</title>
    
    
        <meta property="og:site_name" content="王鹏的技术博客">
    
    
        <meta property="article:author" content="王 鹏">
    
    
        <link rel="icon" href="/mhexo/img/favicon.ico">
    
    
<link rel="stylesheet" href="/mhexo/css/minireset.min.css">

    
<link rel="stylesheet" href="/mhexo/css/all.min.css">

    
<link rel="stylesheet" href="/mhexo/css/csshake.min.css">

    
<link rel="stylesheet" href="/mhexo/css/hljs/lioshi.css">

    
<link rel="stylesheet" href="/mhexo/css/jquery.fancybox.min.css">

    
<link rel="stylesheet" href="/mhexo/styl/main.css">

    
<script src="/mhexo/js/jquery.min.js"></script>

    
<script src="/mhexo/js/highlight.min.js"></script>

    
<script src="/mhexo/js/jquery.fancybox.min.js"></script>

    
<script src="/mhexo/js/clipboard.min.js"></script>

<meta name="generator" content="Hexo 5.2.0"></head>
<body>
    <header>
  <div class="outer">
    <div class="inner">
      <h1 class="logo-wrap">
        <a>王鹏的技术博客<b><sup>1.1</sup></b></a>
      </h1>
    </div>
    <div class="inner">
      <nav class="main-nav">
        
          <a href="/mhexo/">首页</a>
        
          <a href="/mhexo/archives">归档</a>
        
          <a href="/mhexo/categories">分类</a>
        
          <a href="/mhexo/tags">标签</a>
        
          <a href="/mhexo/friends">友链</a>
        
          <a href="/mhexo/about">关于</a>
        
          <a target="_blank" rel="noopener" href="https://www.baidu.com">百度</a>
        
      </nav>
    </div>
  </div>
</header>
    <div class="content">
        <section class="outer">
    <article>
        <div class="article-title">
    <h2>
        <a href="/mhexo/2020/12/21/python%E9%9B%86%E5%90%88set%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86_%E4%BB%8E%E9%9B%B6%E5%AD%A6Python%EF%BC%9A%E7%AC%AC%E5%8D%81%E5%9B%9B%E8%AF%BE-%E5%B8%B8%E7%94%A8%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E9%9B%86%E5%90%88/" class="shake shake-little" title="python集合set底层原理">
            
            python集合set底层原理
        </a>
    </h2>
    <div class="meta_box">
    
        
        
            
                
        
        <div class="meta meta_auth">
            <img src="/mhexo/img/default.png" alt="head" />
            <p>anonymous</p>
        </div>
    
        
        
        <div class="meta meta_date">
            <i class="fas fa-calendar-alt fa-fw" aria-hidden="true"></i>
            <p>发布于：2020年12月21日</p>
        </div>
    
        <div class="meta meta_update">
            <i class="fas fa-edit fa-fw" aria-hidden="true"></i>
            <p>更新于：2020年12月21日</p>
        </div>
    </div>
    

</div>

        <hr>
        <div class="article-entry">
            
            
            
            <h1 id="python集合set底层原理"><a href="#python集合set底层原理" class="headerlink" title="python集合set底层原理"></a>python集合set底层原理</h1><p>在学习了列表和元组之后，我们再来学习一种容器型的数据类型，它的名字叫集合(set)。说到集合这个词大家一定不会陌生，在数学课本上就有这个概念。通常我们对集合的定义是“把一定范围的、确定的、可以区别的事物当作一个整体来看待”，集合中的各个事物通常称为集合的元素。集合应该满足以下特性：</p>
<p>无序性：一个集合中，每个元素的地位都是相同的，元素之间是无序的。</p>
<p>互异性：一个集合中，任何两个元素都认为是不相同的，即每个元素只能出现一次。</p>
<p>确定性：给定一个集合，任给一个元素，该元素或者属于或者不属于该集合，二者必居其一，不允许有模棱两可的情况出现。</p>
<p>Python程序中的集合跟数学上的集合是完全一致的，需要强调的是上面所说的无序性和互异性。无序性说明集合中的元素并不像列中的元素那样一个挨着一个，可以通过索引的方式实现随机访问(随机访问指的是给定一个有效的范围，随机抽取出一个数字，然后通过这个数字获取到对应的元素)，所以Python中的集合肯定不能够支持索引运算。另外，集合的互异性决定了集合中不能有重复元素，这一点也是集合区别于列表的关键，说得更直白一些就是，Python中的集合类型具有去重特性。当然，Python中的集合一定是支持in和not  in成员运算的，这样就可以确定一个元素是否属于集合，也就是上面所说的集合的确定性。集合的成员运算在性能上要优于列表的成员运算，这是集合的底层存储特性决定的，此处我们暂时不做讨论，先记下这个结论即可。</p>
<p>创建集合</p>
<p>在Python中，创建集合可以使用{}字面量语法，{}中需要至少有一个元素，因为没有元素的{}并不是空集合而是一个空字典，我们下一节课就会大家介绍字典的知识。当然，也可以使用内置函数set来创建一个集合，准确的说set并不是一个函数，而是创建集合对象的构造器，这个知识点我们很快也会讲到，现在不理解跳过它就可以了。要创建空集合可以使用set()；也可以将其他序列转换成集合，例如：set(‘hello’)会得到一个包含了4个字符的集合(重复的l会被去掉)。除了这两种方式，我们还可以使用生成式语法来创建集合，就像我们之前用生成式创建列表那样。要知道集合中有多少个元素，还是使用内置函数len；使用for循环可以实现对集合元素的遍历。</p>
<p># 创建集合的字面量语法(重复元素不会出现在集合中)</p>
<p>set1 = {1, 2, 3, 3, 3, 2}</p>
<p>print(set1) # {1, 2, 3}</p>
<p>print(len(set1)) # 3</p>
<p># 创建集合的构造器语法(后面会讲到什么是构造器)</p>
<p>set2 = set(‘hello’)</p>
<p>print(set2) # {‘h’, ‘l’, ‘o’, ‘e’}</p>
<p># 将列表转换成集合(可以去掉列表中的重复元素)</p>
<p>set3 = set([1, 2, 3, 3, 2, 1])</p>
<p>print(set3) # {1, 2, 3}</p>
<p># 创建集合的生成式语法(将列表生成式的[]换成{})</p>
<p>set4 = {num for num in range(1, 20) if num % 3 == 0 or num % 5 == 0}</p>
<p>print(set4) # {3, 5, 6, 9, 10, 12, 15, 18}</p>
<p># 集合元素的循环遍历</p>
<p>for elem in set4:</p>
<p>print(elem)</p>
<p>需要提醒大家，集合中的元素必须是hashable类型。所谓hashable类型指的是能够计算出哈希码的数据类型，你可以暂时将哈希码理解为和变量对应的唯一的ID值。通常不可变类型都是hashable类型，如整数、浮点、字符串、元组等，而可变类型都不是hashable类型，因为可变类型无法确定唯一的ID值，所以也就不能放到集合中。集合本身也是可变类型，所以集合不能够作为集合中的元素，这一点请大家一定要注意。</p>
<p>集合的运算</p>
<p>Python为集合类型提供了非常丰富的运算符，主要包括：成员运算、交集运算、并集运算、差集运算、比较运算(相等性、子集、超集)等。</p>
<p>成员运算</p>
<p>可以通过成员运算in和not in检查元素是否在集合中，代码如下所示。</p>
<p>set1 = {11, 12, 13, 14, 15}</p>
<p>print(10 in set1) # False</p>
<p>print(15 in set1) # True</p>
<p>set2 = {‘Python’, ‘Java’, ‘Go’, ‘Swift’}</p>
<p>print(‘Ruby’ in set2) # False</p>
<p>print(‘Java’ in set2) # True</p>
<p>交并差运算</p>
<p>Python中的集合跟数学上的集合一样，可以进行交集、并集、差集等运算，而且可以通过运算符和方法调用两种方式来进行操作，代码如下所示。</p>
<p>set1 = {1, 2, 3, 4, 5, 6, 7}</p>
<p>set2 = {2, 4, 6, 8, 10}</p>
<p># 交集</p>
<p># 方法一: 使用 &amp; 运算符</p>
<p>print(set1 &amp; set2) # {2, 4, 6}</p>
<p># 方法二: 使用intersection方法</p>
<p>print(set1.intersection(set2)) # {2, 4, 6}</p>
<p># 并集</p>
<p># 方法一: 使用 | 运算符</p>
<p>print(set1 | set2) # {1, 2, 3, 4, 5, 6, 7, 8, 10}</p>
<p># 方法二: 使用union方法</p>
<p>print(set1.union(set2)) # {1, 2, 3, 4, 5, 6, 7, 8, 10}</p>
<p># 差集</p>
<p># 方法一: 使用 - 运算符</p>
<p>print(set1 - set2) # {1, 3, 5, 7}</p>
<p># 方法二: 使用difference方法</p>
<p>print(set1.difference(set2)) # {1, 3, 5, 7}</p>
<p># 对称差</p>
<p># 方法一: 使用 ^ 运算符</p>
<p>print(set1 ^ set2) # {1, 3, 5, 7, 8, 10}</p>
<p># 方法二: 使用symmetric_difference方法</p>
<p>print(set1.symmetric_difference(set2)) # {1, 3, 5, 7, 8, 10}</p>
<p># 方法三: 对称差相当于两个集合的并集减去交集</p>
<p>print((set1 | set2) - (set1 &amp; set2)) # {1, 3, 5, 7, 8, 10}</p>
<p>通过上面的代码可以看出，对两个集合求交集，&amp;运算符和intersection方法的作用是完全相同的，使用运算符的方式更直观而且代码也比较简短。相信大家对交集、并集、差集、对称差这几个概念是比较清楚的，如果没什么印象了可以看看下面的图。</p>
<p>集合的交集、并集、差集运算还可以跟赋值运算一起构成复合运算，如下所示。</p>
<p>set1 = {1, 3, 5, 7}</p>
<p>set2 = {2, 4, 6}</p>
<p># 将set1和set2求并集再赋值给set1</p>
<p># 也可以通过set1.update(set2)来实现</p>
<p>set1 |= set2</p>
<p>print(set1) # {1, 2, 3, 4, 5, 6, 7}</p>
<p>set3 = {3, 6, 9}</p>
<p># 将set1和set3求交集再赋值给set1</p>
<p># 也可以通过set1.intersection_update(set3)来实现</p>
<p>set1 &amp;= set3</p>
<p>print(set1) # {3, 6}</p>
<p>比较运算</p>
<p>两个集合可以用==和!=进行相等性判断，如果两个集合中的元素完全相同，那么==比较的结果就是True，否则就是False。如果集合A的任意一个元素都是集合B的元素，那么集合A称为集合B的子集，即对于∀a∈A，均有a∈B，则A⊆B。A是B的子集，反过来也可以称B是A的超集。如果A是B的子集且A不等于B，那么A就是B的真子集。Python为集合类型提供了判断子集和超集的运算符，其实就是我们非常熟悉的运算符，代码如下所示。</p>
<p>set1 = {1, 3, 5}</p>
<p>set2 = {1, 2, 3, 4, 5}</p>
<p>set3 = set2</p>
<p># </p>
<p>print(set1 &lt; set2, set1 &lt;= set2) # True True</p>
<p>print(set2 &lt; set3, set2 &lt;= set3) # False True</p>
<p># 通过issubset方法也能进行子集判断</p>
<p>print(set1.issubset(set2)) # True</p>
<p># 反过来可以用issuperset或&gt;运算符进行超集判断</p>
<p>print(set2.issuperset(set1)) # True</p>
<p>print(set2 &gt; set1) # True</p>
<p>集合的方法</p>
<p>Python中的集合是可变类型，我们可以通过集合类型的方法为集合添加或删除元素。</p>
<p># 创建一个空集合</p>
<p>set1 = set()</p>
<p># 通过add方法添加元素</p>
<p>set1.add(33)</p>
<p>set1.add(55)</p>
<p>set1.update({1, 10, 100, 1000})</p>
<p>print(set1) # {33, 1, 100, 55, 1000, 10}</p>
<p># 通过discard方法删除指定元素</p>
<p>set1.discard(100)</p>
<p>set1.discard(99)</p>
<p>print(set1) # {1, 10, 33, 55, 1000}</p>
<p># 通过remove方法删除指定元素，建议先做成员运算再删除</p>
<p># 否则元素如果不在集合中就会引发KeyError异常</p>
<p>if 10 in set1:</p>
<p>set1.remove(10)</p>
<p>print(set1) # {33, 1, 55, 1000}</p>
<p># pop方法可以从集合中随机删除一个元素并返回该元素</p>
<p>print(set1.pop())</p>
<p># clear方法可以清空整个集合</p>
<p>set1.clear()</p>
<p>print(set1) # set()</p>
<p>如果要判断两个集合有没有相同的元素可以使用isdisjoint方法，代码如下所示。</p>
<p>set1 = {‘Java’, ‘Python’, ‘Go’, ‘Kotlin’}</p>
<p>set2 = {‘Kotlin’, ‘Swift’, ‘Java’, ‘Objective-C’, ‘Dart’}</p>
<p>set3 = {‘HTML’, ‘CSS’, ‘JavaScript’}</p>
<p>print(set1.isdisjoint(set2)) # False - set1和set2有相同元素</p>
<p>print(set1.isdisjoint(set3)) # True - set1和set3没有相同元素</p>
<p>不可变集合</p>
<p>Python中还有一种不可变类型的集合，名字叫frozenset。set跟frozenset的区别就如同list跟tuple的区别，frozenset由于是不可变类型，能够计算出哈希码，因此它可以作为set中的元素。除了不能添加和删除元素，frozenset在其他方面跟set基本是一样的，下面的代码简单展示了frozenset的用法。</p>
<p>set1 = frozenset({1, 3, 5, 7})</p>
<p>set2 = frozenset(range(1, 6))</p>
<p>print(set1 &amp; set2) # frozenset({1, 3, 5})</p>
<p>print(set1 | set2) # frozenset({1, 2, 3, 4, 5, 7})</p>
<p>print(set1 - set2) # frozenset({7})</p>
<p>print(set1 &lt; set2) # False</p>
<p>简单的总结</p>
<p>Python中的集合底层使用了哈希存储的方式，对于这一点我们暂时不做介绍，在后面的课程有需要的时候再为大家讲解集合的底层原理，现阶段大家只需要知道集合是一种容器，元素必须是hashable类型，与列表不同的地方在于集合中的元素没有序、不能用索引运算、不能重复。返回搜狐，查看更多</p>

        </div>
        <div class="article-copyright">
            
    <blockquote>
        <p>
            版权声明：本文为「子曰无衣」的原创文章，博客内容遵循 署名-非商业性使用-相同方式共享 协议。<br>本文永久链接是：https://wangpdizhi.gitee.io/mhexo/2020/12/21/python%E9%9B%86%E5%90%88set%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86_%E4%BB%8E%E9%9B%B6%E5%AD%A6Python%EF%BC%9A%E7%AC%AC%E5%8D%81%E5%9B%9B%E8%AF%BE-%E5%B8%B8%E7%94%A8%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E9%9B%86%E5%90%88/
        </p>
    </blockquote>


        </div>
    </article>
    

    <section id="toc-div" >
        <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#python%E9%9B%86%E5%90%88set%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86"><span class="toc-text">python集合set底层原理</span></a></li></ol>
    </section>
    <section id="gohome" style="display: none;"><a>顶</a></section>

    <script>
        function get_top_by_link(link){
            var hnid = "#" + $(link).attr("data");
            if ($(hnid).length > 0){
                return $(hnid).offset().top;
            }else{
                return 0;
            }
        }
        //go to hn
        function gotohn(link){
            $("html,body").animate({scrollTop: get_top_by_link(link) }, 300);
        }
        //页面滚动
        function update(){
            var scrollH = $(window).scrollTop();
            if($(".toc-link")){
                $(".toc-link").each(function(i,link){
                    var mdHeight = get_top_by_link(link);
                    if(mdHeight <= scrollH + 40){
                        //高亮导航菜单
                        $('.toc-link').removeClass('on');
                        $(link).addClass('on');
                    }
                });
            }
            //返回顶部显隐
            if(scrollH < 200){
                $("#gohome").css("display","none");
            }else{
                $("#gohome").css("display","block");
            }
        }
        $(function(){
            //修复部分锚点从属关系
            if($("#toc-div >li").length > 0){
                $("#toc-div >li").appendTo($("#toc-div >ol:first"));
            }
            //返回顶部
            $('#gohome').click(function(){
                $("html,body").animate({scrollTop: 0}, 300);
                return false;
            })
            //遍历锚点
            $(".toc-link").each(function(i,link){
                $(link).attr("data",$(link).attr('href').substring(1));
                $(link).attr("href","javascript:void(0);");
                $(link).attr("onclick","gotohn(this);");
            })
            //绑定滚动事件
            $(window).bind('scroll', update);
            //初始化toc
            var first_toc = $(".toc-link")[0];
            var first_scroll = get_top_by_link(first_toc);
            var window_scroll = $(window).scrollTop();
            if(window_scroll <= first_scroll){
                $(first_toc).addClass('on');
            }
        })
    </script>

</section>
    </div>
    <footer>
    <div class="outer">
        <div class="inner">
            Powered by <a href="http://prowiki.demopage.icu/" target="_blank">ProWiki</a>
            &copy;2020 子曰无衣<br>
            <a target="_blank" rel="noopener" href="http://beian.miit.gov.cn/">冀ICP备00000000号</a>
        </div>
    </div>
</footer>

<script src="/mhexo/js/custom.js"></script>


    <script>onload_content();</script>

</body>
</html>
